
/**
 * 扩展DOM元素
 * 
 * @param {DOM} ele 带扩展元素
 */
function extendElement( ele ){
  return Object.assign( ele, {

    /**
     * 把当前元素插入到ref指定的位置
     * @param {} ref
     *  - {string} 元素标识字符串(css selector格式)
     *  - {DOM} DOM对象 
     * @param {str} position 插入位置说明
     *  - "beforebegin"
     *  - "afterbegin"
     *  - "beforeend"
     *  - "afterend"
     *  - "replace" : 使用当前元素替换掉ref元素
     */
    insertInto : ( ref, position = "beforeend" ) => {
      /** 输入参数处理 */
      if( typeof ref==="object" && ref.nodeType && ref.nodeType==1 ){
        ref = ref
      }else if( typeof ref==="string" ){
        ref = document.querySelector( ref )
      }

      /** 插入动作 */
      if( position == "replace" ){
        ref.parentElement.replaceChild( ele, ref )
      }else
        ref.insertAdjacentElement( position, ele )

      return ele
    }

  } )
}


/**
 * 构建DOM对象(树)
 * 
 * Syntax
 *  html( element, attribute:Dict={}, *children) : 新构建组件的dom
 *  html( element, parent, attribute:Dict={}, *children) : parent的dom
 *  html( element, parent, insertPosition, attribute:Dict={}, *children ) : parent的dom
 *  html( element, parent, replaceChildNode, attribute:Dict={}, *children ) : parent的dom
 * 
 * Arguments
 * @element 创建元素
 *  - {string} 元素标识字符串。Syntan：{namespace}ele1 > ele2 > ... > eleN
 * @parent 父元素。若忽略该参数，则创建的element添加到parent中
 *  - {string}  父元素对应的css-select字符串
 *  - {DOM} 父元素DOM对象
 *  - Note: 该参数不能填写null。不需要的时候，直接忽略该参数，使用第二个函数Pattern
 * @insertPosition {string}: 指示新建的元素(element)相对已有元素parent的关系
 *  - 'beforebegin' : element插入到parent元素前。相当于document.insertBefore( parent, element )
 *  - 'afterbegin' : element插入到parent元素所有子元素前。相当于parent.prependChild(element)
 *  - 'beforeend' : element插入到parent元素所有子元素后。相当于parent.appendChild(element)
 *  - 'afterend' : element插入到parent元素后。相当于document.insertBefore( element, parent )
 *  - 'replaceLast' : element替换parent的最后一个元素（若没有则新加）。相当于parent.replaceChild( element, parent.lastChild )
 *  - 'replaceFirst' : element替换parent的第一个子元素（若没有则新加）。相当于parent.replaceChild( element, parent.firstChild )
 *  - 'replaceParent' : element替换parent元素。
 * @attribute 元素属性。Must
 *  - Syntax：{attriName:attriValue, ...}
 *  - 必须存在。不存在使用{}占位
 *  - 事件属性。Syntax：strEventName:(e)=>{} or strEventName:()=>{}
 *    - 支持的strEventName: click / change / 
 *  - 属性值为null，表示不需要该属性
 *    - 利用该特性，可以为元素增加默认的类似checked的属性
 *  - 属性值为Boolean，表示标识属性。
 *    - 常见标识属性：checked / disabled /
 * @children 子元素
 *  - {string} or {number} Text
 *  - {DOM}
 *  - {Array[Array[Array[...]]]} children列表。一直嵌套到其它类型为止
 *  - null  不添加的元素。可以利用该特性结合?:操作符来选择性添加子元素
 * @returns 构建的顶层DOM元素。即element的DOM对象
 * 
 */
function html(){
  // 函数域变量定义
  let nxtPos = 1, narg = arguments[nxtPos], parent = null, rtn = null, ele = null
  

  // ========  父元素识别
  if( typeof narg === "string" ){   // 父元素以“cssselect”形式呈现。Note: 因为nxtPos开始就为1，所以这里实际在检查第二个参数
    parent = document.querySelector(arguments[nxtPos])    // parent脱离null
    if( parent ) nxtPos++     // 过滤掉narg不是父元素id的应用
  }else if( typeof narg==="object" && narg.nodeType && narg.nodeType==1 ){  // 父元素以ElementNode形式呈现。第二个参数为ElementNode
    parent = arguments[nxtPos++]    // parent脱离null，并且nxtPos向后推进
  }


  // ========  当前元素创建
  // Namespace解析
  console.assert( typeof arguments[0] === "string" )    // 输入参数错误过滤。这里对第一个参数进行解析
  const rst = arguments[0].match(/({([^}]*)})?(.*)/)      // \2为namespace；\3为tagNameList
  const isNS = rst[2] ? true : false

  // 创建层次化系列元素。这里的思路为：构建解析完当前元素和父元素后，先把它们之间的关系搞好
  let tagNames = rst[3].split('>'), first = true
  // console.log(`arguments[0]=${arguments[0]}; tagString=${rst[3]}; tagNames=${tagNames}`)
  for( let i=0; tagNames.length>i; i++  ){     // 切割到各个TagNames数组

    // 过滤异常
    const tagName = tagNames[i].trim()
    if( tagName == "" ) continue

    ele = isNS ? document.createElementNS(rst[2], tagName) : document.createElement(tagName)    // 创建元素

    if( parent ){   // 按构建的链式结构逐个加入父元素链

      if( i==0 ){   // 用户parent添加判断: 支持插入到parent的位置可设置

        if( typeof arguments[nxtPos] === "string" ){    // 把新建元素插入到用户给定的parent元素的arguments[nxtPos]指定位置
          
          const insertPosition = ['beforebegin', 'afterbegin', 'beforeend', 'afterend']   // 允许的参数
          if( insertPosition.includes( arguments[nxtPos] ) ){   // 是位置标识符
            parent.insertAdjacentElement( arguments[nxtPos], ele )  // 按照位置插入
            nxtPos += 1
          }else if( arguments[nxtPos] === "replaceFirst" ){
            if( parent.firstChild )
              parent.replaceChild( ele, parent.firstChild )
            else
              parent.appendChild( ele )
            nxtPos += 1
          }else if( arguments[nxtPos] === "replaceLast" ){
            if( parent.lastChild )
              parent.replaceChild( ele, parent.lastChild )
            else
              parent.appendChild( ele )
            nxtPos += 1
          }else if( arguments[nxtPos] === "replaceParent" ){
            parent.parentElement.replaceChild( ele, parent )
            nxtPos += 1
          }
          
        }else{
          parent.appendChild(ele);  // 把新建元素插入到用户给定的parent元素的最后
        }

      }else
        parent.appendChild(ele);  // 各个'>'层级父-子关系串链
    }

    if( first ){ rtn = ele; first = false }   // 保存返回对象
    parent = ele      // 父子链
  }

  // ========  属性添加
  let attris = arguments[nxtPos]    // 获取属性
  if( 1 >= arguments.length ){
  }else if( typeof attris !== "string" && typeof attris === "object" && !(attris.nodeType==1) ){   // 非字符串和Node类型

    for( let key in attris ){

      const attr = attris[key]

      if( attr === null || attr === undefined ) continue    // 无效属性不添加

      // 添加事件
      if( typeof attr === 'function' ){    // 事件添加

        if( ["change", "click"].includes( key ) ){
          ele.addEventListener(key, attr)
        }else{
          console.warn( `html: 不支持的事件名(${key})` )
        }
        
      }else if( typeof attr === 'boolean' ){
        ele[key] = attr
      }else{
        ele.setAttribute(key, attr)    // 添加属性
      }
    }
    nxtPos += 1

  }

  

  // ========  添加子元素
  function appendChild(p, lst){
    for( let c of lst ){
      if( typeof c === "number" ){
        text = document.createTextNode(String(c))
        p.appendChild(text)
      }else if( typeof c === "string" ){    // c为string。添加TextNode
        text = document.createTextNode(c)
        p.appendChild(text)
      }else if( Array.isArray(c) ){   // c为array。内部包含子DOM Element
        appendChild( p, c )
      }else if( c == null || c == undefined ){
          // null、undefined表示不需要添加
      }else if( c.nodeType && c.nodeType==1 ){    // c为DOM Element
        p.appendChild(c)
    /*  }else if( c instanceof _INNER_XML_ ){
        p.insertAdjacentHTML('beforeend', c.innerXML)*/
      }else{
        console.error(`'html' function cannot support this type of [${c} and type: ${(typeof c)}] in children list: [${lst}] for appendChild`)
      }
    }
  }
  appendChild( ele, Array.from(arguments).slice(nxtPos) )    // 添加子元素操作

  return  extendElement( rtn ) // 返回当前元素DOM
}




/** ****************************************************************************************************************************************
 * 
 *******************************************************************************************************************************************/

/**
 * 下拉UI组件
 * 
 * @param {str} id : 组件名称
 * @param {str} title : 按钮显示文字
 * @param {[type]} options : 每个选项的{title,checked}
 *  - {title, checked} : 
 *  - {DOM}
 * @param {function} 选择事件
 * 
 * 组件内容
 *  <div id="xdropdown_{id}" class="xdropdown">
 *    <button id="xdropdown_btn_{id}">{title}▼</button>
 *    <div id="xdropdown_option_{id}">
 *      <label><input type="checkbox" name="{option1}">{option1}</label>
 *    </div>
 *  </div>
 */
function XDropDown( id, title, options, event ){

  if( !window.__XDropDown__ ){

    /* 注册CSS风格 */
    const style = document.head.appendChild( document.createElement('style') )
    style.textContent = `
      .xdropdown div[id^='xdropdown_option_'] {
        display : none;
        position : absolute;
        background : white;
        border : 1px solid #ddd;
        z-index : 1;
      }

      .xdropdown label{
        display : block;
        padding : 8px;
      }
    `

    /* 点击外部，回收CheckBox列表 */
    document.addEventListener( 'click', function(e){
      document.querySelectorAll(`div[id^="xdropdown_option_"]`).forEach( dd=>{
        const btn = dd.parentElement.querySelector('button')
        if( btn && !dd.contains(e.target) && !btn.contains(e.target) ){
          dd.style.display = "none"
        }
      })
    } )

    window.__XDropDown__ = true
  }

  return html( 'div', {id:`xdropdown_${id}`, class:'xdropdown'},
    html( 'button', {id:`xdropdown_btn_${id}`, 'click':(e)=>{
      const content = e.target.parentElement.querySelector(`div`)   // 获取父元素，然后获取内容元素
      content.style.display = content.style.display==="block" ? "none" : "block"    // 切换内容显示
    }}, `${title}▼` ),
    html( 'div', {id:`xdropdown_option_${id}`, style:"display:none;"}, 
      options.map( opt => {
        if( typeof opt==="object" && opt.nodeType && opt.nodeType==1 ){

          return opt

        }else if( typeof opt==="object" ){

          return html( 'label', {}, 
            html( 'input', {type:'checkbox', name:opt.title, checked:opt.checked, 'change':event} ),
            opt.title
          )

        }
      })
    )
  )
}

